package com.shitou.springai1.user.service;

import com.shitou.springai1.admin.entity.dto.ImageDto;
import com.shitou.springai1.admin.service.ImagesService;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import redis.clients.jedis.search.*;
import redis.clients.jedis.search.schemafields.SchemaField;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 图片搜索服务类，提供图片的搜索、索引和存储功能
 * 使用Redis和RediSearch实现高效的图片搜索
 */
@Service
@RequiredArgsConstructor
public class ImageSearchService {


    @Value("${app.redis.index-name}")
    private String INDEX_NAME;
    @Value("${app.redis.doc-prefix}")
    private String DOC_PREFIX;



    // 依赖注入
    private final RedisTemplate<String, Object> redisTemplate;  // Redis操作模板
    private final ImagesService imagesService;  // 图片服务，用于从MySQL获取数据
    private final RediSearchCommands searchCommands;  // RediSearch命令操作接口

    /**
     * 服务初始化方法
     * 1. 创建索引（如果不存在）
     * 2. 从MySQL重新加载所有图片数据
     * 3. 测试搜索功能
     */
    @PostConstruct
    public void init() {
        createIndexIfNotExists();
        reloadAllImagesFromMySQL();
        // 测试搜索功能
        testSearchFunctionality();
    }

    /**
     * 测试搜索功能
     * 1. 测试搜索所有文档
     * 2. 测试简单搜索
     */
    private void testSearchFunctionality() {
        try {
            // 测试搜索所有文档
            SearchResult allResults = searchCommands.ftSearch(INDEX_NAME, "*");
            System.out.println("索引中总文档数: " + allResults.getTotalResults());

            // 测试简单搜索
            if (allResults.getTotalResults() > 0) {
                SearchResult testResults = searchCommands.ftSearch(INDEX_NAME, "测试");
                System.out.println("测试搜索 '测试' 找到: " + testResults.getTotalResults() + " 个结果");
            }
        } catch (Exception e) {
            System.err.println("搜索功能测试失败: " + e.getMessage());
        }
    }

    /**
     * 创建索引（如果不存在）
     * 使用RediSearch创建全文搜索索引
     */
    private void createIndexIfNotExists() {
        try {
            searchCommands.ftInfo(INDEX_NAME);
            System.out.println("索引已存在");
        } catch (Exception e) {
            try {
                // 正确定义索引字段
                Schema schema = new Schema()
                        .addTextField("imagesName", 5.0)  // 图片名称字段，权重5.0
                        .addTextField("imagesUrl", 1.0)   // 图片URL字段，权重1.0
                        .addTextField("description", 2.0) // 描述字段，权重2.0
                        .addTextField("keywords", 3.0);   // 关键词字段，权重3.0

                FTCreateParams params = FTCreateParams.createParams()
                        .on(IndexDataType.HASH)  // 使用Hash数据类型
                        .addPrefix(DOC_PREFIX);   // 设置文档前缀

                searchCommands.ftCreate(INDEX_NAME, params, (Iterable<SchemaField>) schema);
                System.out.println("创建新索引成功");
            } catch (Exception ex) {
                System.err.println("创建索引失败: " + ex.getMessage());
                ex.printStackTrace();
            }
        }
    }

    /* -------------------------------------------------
     * 修复搜索方法 - 使用正确的 RediSearch 语法
     * ------------------------------------------------- */
    /**
     * 搜索图片方法
     * @param query 搜索查询字符串
     * @return 匹配的图片列表
     */
    public List<ImageDto> searchImages(String query) {
        List<ImageDto> results = new ArrayList<>();

        if (query == null || query.trim().isEmpty()) {
            return results;
        }

        try {
            // 清理和验证查询字符串
            String safeQuery = sanitizeQuery(query);
            if (safeQuery.isEmpty()) {
                System.err.println("查询字符串清理后为空");
                return results;
            }

            System.out.println("原始查询: " + query);
            System.out.println("安全查询: " + safeQuery);

            // 尝试不同的搜索策略
            List<SearchStrategy> searchStrategies = Arrays.asList(
                    new SimpleSearchStrategy(safeQuery),
                    new PrefixSearchStrategy(safeQuery),
                    new WildcardSearchStrategy(safeQuery),
                    new MultiFieldSearchStrategy(safeQuery),
                    new PhraseSearchStrategy(safeQuery)
            );

            for (SearchStrategy strategy : searchStrategies) {
                try {
                    System.out.println("尝试搜索策略: " + strategy.getDescription());
                    SearchResult searchResults = searchCommands.ftSearch(INDEX_NAME, strategy.getQuery());

                    if (searchResults.getTotalResults() > 0) {
                        System.out.println("找到 " + searchResults.getTotalResults() + " 个结果");

                        for (Document doc : searchResults.getDocuments()) {
                            try {
                                ImageDto dto = convertDocumentToImageDto(doc);
                                if (dto != null && !containsImage(results, dto)) {
                                    results.add(dto);
                                }
                            } catch (Exception e) {
                                System.err.println("转换文档失败: " + e.getMessage());
                            }
                        }

                        if (!results.isEmpty()) {
                            break; // 找到结果就停止尝试其他策略
                        }
                    }
                } catch (Exception e) {
                    System.err.println("搜索策略失败: " + strategy.getDescription() + ", 错误: " + e.getMessage());
                    // 继续尝试下一个策略
                }
            }

            // 如果没有找到结果，尝试更简单的搜索
            if (results.isEmpty()) {
                System.out.println("尝试简单关键词搜索");
                try {
                    // 提取可能的关键词进行搜索
                    List<String> keywords = extractKeywords(safeQuery);
                    for (String keyword : keywords) {
                        if (keyword.length() > 1) { // 避免太短的关键词
                            SearchResult searchResults = searchCommands.ftSearch(INDEX_NAME, keyword);
                            for (Document doc : searchResults.getDocuments()) {
                                ImageDto dto = convertDocumentToImageDto(doc);
                                if (dto != null && !containsImage(results, dto)) {
                                    results.add(dto);
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    System.err.println("关键词搜索失败: " + e.getMessage());
                }
            }

        } catch (Exception e) {
            System.err.println("搜索失败: " + e.getMessage());
            e.printStackTrace();
        }

        System.out.println("最终返回结果数量: " + results.size());
        return results;
    }

    /* -------------------------------------------------
     * 搜索策略接口和实现类
     * ------------------------------------------------- */
    /**
     * 搜索策略接口
     * 定义不同搜索策略的通用方法
     */
    private interface SearchStrategy {
        String getQuery();      // 获取查询字符串
        String getDescription(); // 获取策略描述
    }

    /**
     * 简单搜索策略实现
     */
    private class SimpleSearchStrategy implements SearchStrategy {
        private final String query;

        public SimpleSearchStrategy(String query) {
            this.query = query;
        }

        @Override
        public String getQuery() {
            return query;
        }

        @Override
        public String getDescription() {
            return "简单搜索: " + query;
        }
    }

    /**
     * 前缀搜索策略实现
     */
    private class PrefixSearchStrategy implements SearchStrategy {
        private final String query;

        public PrefixSearchStrategy(String query) {
            this.query = query;
        }

        @Override
        public String getQuery() {
            return query + "*";
        }

        @Override
        public String getDescription() {
            return "前缀搜索: " + query + "*";
        }
    }

    /**
     * 通配符搜索策略实现
     */
    private class WildcardSearchStrategy implements SearchStrategy {
        private final String query;

        public WildcardSearchStrategy(String query) {
            this.query = query;
        }

        @Override
        public String getQuery() {
            return "*" + query + "*";
        }

        @Override
        public String getDescription() {
            return "通配符搜索: *" + query + "*";
        }
    }

    /**
     * 多字段搜索策略实现
     */
    private class MultiFieldSearchStrategy implements SearchStrategy {
        private final String query;

        public MultiFieldSearchStrategy(String query) {
            this.query = query;
        }

        @Override
        public String getQuery() {
            // 正确的多字段搜索语法：使用括号
            return "(@imagesName:" + query + ") | (@description:" + query + ") | (@keywords:" + query + ")";
        }

        @Override
        public String getDescription() {
            return "多字段搜索: " + query;
        }
    }

    /**
     * 短语搜索策略实现
     */
    private class PhraseSearchStrategy implements SearchStrategy {
        private final String query;

        public PhraseSearchStrategy(String query) {
            this.query = query;
        }

        @Override
        public String getQuery() {
            // 短语搜索，使用双引号
            return "\"" + query + "\"";
        }

        @Override
        public String getDescription() {
            return "短语搜索: \"" + query + "\"";
        }
    }

    /* -------------------------------------------------
     * 辅助方法
     * ------------------------------------------------- */

    // 检查是否已包含相同的图片
    private boolean containsImage(List<ImageDto> list, ImageDto dto) {
        return list.stream().anyMatch(item ->
                item.getImagesUrl() != null && item.getImagesUrl().equals(dto.getImagesUrl()));
    }

    // 从 Document 转换为 ImageDto
    private ImageDto convertDocumentToImageDto(Document doc) {

        try {
            Map<String, Object> properties = new HashMap<>();
            doc.getProperties().forEach(prop -> {
                properties.put(prop.getKey(), prop.getValue());
            });

            return convertFromProperties(properties);
        } catch (Exception e) {
            System.err.println("转换文档失败: " + e.getMessage());
            return null;
        }
    }

    private String sanitizeQuery(String query) {
        if (query == null) return "";

        // 移除控制字符和特殊字符
        String cleaned = query.replaceAll("[\\x00-\\x1F\\x7F]", "") // 控制字符
                .replaceAll("[^\\x20-\\x7E\\u4E00-\\u9FA5]", "") // 非打印字符和非中英文
                .trim();

        // 如果清理后为空，尝试提取字母和数字
        if (cleaned.isEmpty()) {
            cleaned = query.replaceAll("[^a-zA-Z0-9\\u4E00-\\u9FA5]", "").trim();
        }

        return cleaned;
    }

    private List<String> extractKeywords(String query) {
        List<String> keywords = new ArrayList<>();

        if (query == null || query.trim().isEmpty()) {
            return keywords;
        }

        // 中文处理：按字符分割（简单处理）
        if (query.matches(".*[\\u4E00-\\u9FA5]+.*")) {
            for (char c : query.toCharArray()) {
                if (Character.toString(c).matches("[\\u4E00-\\u9FA5]")) {
                    keywords.add(Character.toString(c));
                }
            }
        }

        // 英文处理：按空格分割
        String[] words = query.split("\\s+");
        for (String word : words) {
            if (word.length() > 1 && word.matches("[a-zA-Z0-9]+")) {
                keywords.add(word);
            }
        }

        // 如果还是没提取到关键词，返回原始查询（清理后）
        if (keywords.isEmpty() && !query.trim().isEmpty()) {
            keywords.add(query.trim());
        }

        return keywords;
    }

    /* -------------------------------------------------
     * 其他方法保持不变
     * ------------------------------------------------- */
    public void reloadAllImagesFromMySQL() {
        int pageNo = 1;
        int pageSize = 500;
        int totalCount = 0;

        while (true) {
            List<Map<String, Object>> page = imagesService.getImages(pageNo, pageSize, null);
            if (page.isEmpty()) break;

            int savedCount = 0;
            for (Map<String, Object> row : page) {
                ImageDto dto = convertToImageDto(row);
                if (dto != null) {
                    saveSingleImage(dto);
                    savedCount++;
                }
            }

            totalCount += savedCount;
            pageNo++;
        }
        System.out.println("全量数据同步完成，总计 " + totalCount + " 条记录");
    }

    public void saveSingleImage(ImageDto dto) {
        if (dto == null || dto.getImagesUrl() == null) {
            System.err.println("ImageDto 或 imagesUrl 为空，跳过保存");
            return;
        }

        String hashKey = DOC_PREFIX + dto.getImagesUrl();
        Map<String, String> hash = new HashMap<>();

        hash.put("imagesName", null2str(dto.getImagesName()));
        hash.put("imagesUrl", null2str(dto.getImagesUrl()));
        hash.put("description", null2str(dto.getDescription()));

        if (dto.getKeywords() != null && !dto.getKeywords().isEmpty()) {
            String keywordsStr = dto.getKeywords().stream()
                    .filter(Objects::nonNull)
                    .collect(Collectors.joining(","));
            hash.put("keywords", keywordsStr);
        } else {
            hash.put("keywords", "");
        }

        try {
            redisTemplate.opsForHash().putAll(hashKey, hash);
        } catch (Exception e) {
            System.err.println("保存图片数据失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private ImageDto convertToImageDto(Map<String, Object> row) {
        if (row == null || row.isEmpty()) return null;

        try {
            ImageDto dto = new ImageDto();
            dto.setId(Optional.ofNullable(row.get("id"))
                    .map(v -> {
                        if (v instanceof Number) return ((Number) v).intValue();
                        if (v instanceof String) return Integer.parseInt((String) v);
                        return 0;
                    }).orElse(0));
            dto.setImagesId(null2str(row.get("imagesId")));
            dto.setImagesName(null2str(row.get("imagesName")));
            dto.setImagesUrl(null2str(row.get("imagesUrl")));
            dto.setDescription(null2str(row.get("description")));

            Object keywordsObj = row.get("keywords");
            if (keywordsObj instanceof String) {
                String keywordsStr = (String) keywordsObj;
                if (!keywordsStr.isEmpty()) {
                    dto.setKeywords(Arrays.asList(keywordsStr.split(",")));
                } else {
                    dto.setKeywords(new ArrayList<>());
                }
            } else if (keywordsObj instanceof Collection) {
                dto.setKeywords(new ArrayList<>((Collection<String>) keywordsObj));
            } else {
                dto.setKeywords(new ArrayList<>());
            }

            return dto;
        } catch (Exception e) {
            System.err.println("转换 ImageDto 失败: " + e.getMessage());
            return null;
        }
    }

    private ImageDto convertFromProperties(Map<String, Object> properties) {
        if (properties == null) return null;

        ImageDto dto = new ImageDto();
        dto.setImagesName(null2str(properties.get("imagesName")));
        dto.setImagesUrl(null2str(properties.get("imagesUrl")));
        dto.setDescription(null2str(properties.get("description")));

        String keywordsStr = null2str(properties.get("keywords"));
        if (!keywordsStr.isEmpty()) {
            dto.setKeywords(Arrays.asList(keywordsStr.split(",")));
        } else {
            dto.setKeywords(new ArrayList<>());
        }

        return dto;
    }

    private String null2str(Object o) {
        return o == null ? "" : o.toString().trim();
    }

    // 其他方法保持不变...
    public Map<String, Object> getIndexInfo() {
        Map<String, Object> info = new HashMap<>();
        try {
            Map<String, Object> indexInfo = searchCommands.ftInfo(INDEX_NAME);
            info.put("indexExists", true);
            info.put("indexInfo", indexInfo);

            SearchResult result = searchCommands.ftSearch(INDEX_NAME, "*");
            info.put("documentCount", result.getTotalResults());

        } catch (Exception e) {
            info.put("indexExists", false);
            info.put("error", e.getMessage());
        }
        return info;
    }

    @Deprecated
    public void saveImages(ImageDto imagesDto) {
        saveSingleImage(imagesDto);
    }
}
